﻿using System;
using System.Reflection;
using nRoute.Components;
using nRoute.Components.Disposer;
using nRoute.Components.Handlers;
using nRoute.Samples.OfficerXcel.Services;
using nRoute.Samples.OfficerXcel.Views;
using nRoute.ViewModels;
using nRoute.Samples.OfficerXcel.Model;
using nRoute.Components.Composition;
using System.ComponentModel;

namespace nRoute.Samples.OfficerXcel.ViewModels
{
    [MapViewModel(typeof(WorksheetView))]
    public class WorksheetViewModel 
        : ViewModelBase
    {

#region Declarations

        private const string CELL_TITLE_FORMAT = "{0}{1}";
        
        private Worksheet _worksheet;
        private PropertyInfo _activeColumnPropertyInfo;
        private Handler<PropertyChangedEventArgs, PropertyChangedEventHandler> _propertyChangedHandler;
        private string _activeCellValue;
        private string _activeColumnHeader;
        private int _activeRowIndex = -1;
        private readonly IActiveWorksheetService _activeWorksheetService;

#endregion

        [ResolveConstructor]
        public WorksheetViewModel(IActiveWorksheetService activeWorksheetService)
        {
            if (activeWorksheetService == null) throw new ArgumentNullException("activeWorksheetService");
            _activeWorksheetService = activeWorksheetService;
            _activeWorksheetService.ActiveWorksheetChanged += (s, e) => SetupWorksheet();
            SetupWorksheet();
        }
        
#region Properties

        public Worksheet Worksheet
        {
            get { return _worksheet; }
            private set
            {
                if (_worksheet != value)
                {
                    _worksheet = value;
                    NotifyPropertyChanged(() => Worksheet);
                    SetupWorksheet();
                }
            }
        }

        public string ActiveColumnHeader
        {
            get { return _activeColumnHeader; }
            set
            {
                _activeColumnHeader = value;
                NotifyPropertyChanged(() => ActiveColumnHeader);
                NotifyPropertyChanged(() => ActiveCellTitle);
                UpdateActiveColumnPropertyInfo();
                UpdateActiveCellValue();
            }
        }

        public int ActiveRowIndex
        {
            get { return _activeRowIndex; }
            set
            {
                _activeRowIndex = value;
                NotifyPropertyChanged(() => ActiveRowIndex);
                NotifyPropertyChanged(() => ActiveCellTitle);
                UpdateActiveCellValue();
                SetupPropertyChangedHandler();
            }
        }

        public string ActiveCellTitle
        {
            get
            {
                if (ActiveRowIndex < 0 || string.IsNullOrEmpty(ActiveColumnHeader))
                {
                    return string.Empty;
                }
                else 
                {
                    return string.Format(CELL_TITLE_FORMAT, ActiveColumnHeader, ActiveRowIndex + 1);
                }
            }
        }

        public string ActiveCellValue
        {
            get
            {
                return _activeCellValue;
            }
            set
            {
                if (!string.Equals(_activeCellValue, value, StringComparison.InvariantCulture))
                {
                    _activeCellValue = value;
                    NotifyPropertyChanged(() => ActiveCellValue);
                    SetActiveCellValue(value);
                }
            }
        }

#endregion

#region Commands Properties

        public ActionCommand LoadedCommand { get; private set; }

#endregion

#region Setup Related

        private void SetupWorksheet()
        {
            Worksheet = _activeWorksheetService.ActiveWorksheet;
            ActiveRowIndex = -1;
            ActiveColumnHeader = string.Empty;
            ActiveCellValue = string.Empty;

            if (Worksheet != null)
            {
                // and attach each row - we wanna know if the content has changed
                foreach (var _row in _worksheet.Rows)
                {
                    var _worksheetRow = _row;
                    _row.PropertyChanged += new Handler<PropertyChangedEventArgs, PropertyChangedEventHandler>(
                        (s, e) => { if (!Worksheet.IsDirty) Worksheet.IsDirty = true; },
                        (h) => _worksheetRow.PropertyChanged -= h).DisposeWith(Worksheet.Disposer);
                }
            }
        }

        private void UpdateActiveColumnPropertyInfo()
        {
            // basic check
            if (ActiveRowIndex < 0 || string.IsNullOrEmpty(ActiveColumnHeader))
            {
                _activeColumnPropertyInfo = null;
                return;
            }

            // we get the property
            _activeColumnPropertyInfo = typeof(WorksheetRow).GetProperty(ActiveColumnHeader);
        }

        private void UpdateActiveCellValue()
        {
            if (_activeRowIndex < 0 || string.IsNullOrEmpty(ActiveColumnHeader) 
                || _activeColumnPropertyInfo == null)
            {
                ActiveCellValue = null;
                return;
            }

            // we get the value and update
            var _activeRow = Worksheet.Rows[_activeRowIndex];
            ActiveCellValue = Convert.ToString(_activeColumnPropertyInfo.GetValue(_activeRow, null));
        }

        private void SetupPropertyChangedHandler()
        {
            // we remove the handler if there is no property 
            if (_activeRowIndex < 0 || string.IsNullOrEmpty(ActiveColumnHeader))
            {
                if (_propertyChangedHandler != null) _propertyChangedHandler.Dispose();
                _propertyChangedHandler = null;
                return;
            }

            // we create a property changed handler
            var _activeRow = Worksheet.Rows[_activeRowIndex];
            _propertyChangedHandler = new Handler<PropertyChangedEventArgs, PropertyChangedEventHandler>((s, e) => 
                {
                    // if the active property changed is the currently selected one
                    if (string.Equals(e.PropertyName, ActiveColumnHeader))
                    {
                        UpdateActiveCellValue();
                    }
                },
                (h) => _activeRow.PropertyChanged -= h);
            _activeRow.PropertyChanged += _propertyChangedHandler;
        }

        private void SetActiveCellValue(string value)
        {
            // we get the active row and update it's value for the active column
            if (_activeRowIndex < 0 || string.IsNullOrEmpty(ActiveColumnHeader)
               || _activeColumnPropertyInfo == null) return;

            var _activeRow = Worksheet.Rows[_activeRowIndex];
            _activeColumnPropertyInfo.SetValue(_activeRow, value, null);
        }

#endregion

    }
}